package mindwave.theoracle;

import com.csvreader.CsvWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

// ReadMindWave.java
// Andrew Davison, ad@fivedots.coe.psu.ac.th, October 2012

/*  The ThinkGeer Communications Driver (TGCD)
 connects to the MindWave as a serial COM device and can read the
 following EEG information:
 * eSense Attention value
 * eSense Meditation value
 * poor signal quality
 * EEG band powers (delta, theta, alpha, beta, gamma)
 * raw EEG wave samples (at 512Hz)
 * blink strength
 * battery setting
    
 The most useful of these are probably:
 Attention, Meditation, and Blink Strength

 An overview of these wave types can be found in the MindWave 
 User Guide in the technology overview section. Attention is a measure
 of the user's focus or concentration. Meditation is a measure of
 calmness/relaxation.

 Attention and Meditation range between 0-100, with 
 higher being stronger. Blink strength ranges between 0-255,
 with higher being stronger 

 More technical info on TGCD can be found in the TGCD section of
 the developer's guide in the unzipped developers tools:
 \MindSet Development Tools\developer_tools_2.1_development_guide.pdf
 */
public class ReadMindWave {

    private static final int COM_PORT = 6;
    // this value depends on your MindWave installation; change as required
    ConcurrentLinkedQueue<experienceBean> queque = new ConcurrentLinkedQueue<experienceBean>();
    // files for logging the MindWave data
    private CsvWriter csv;
    private static final int STRONG_SIGNAL = 50;
    // smaller is stronger, with 0 the most powerful
    private int imageFocus;
    private int connID;
    private ThinkGear tg = ThinkGear.INSTANCE;
    private boolean stop = false;
    private boolean hasBlinked = false;
    /* this flag is used to remember a blink strength update
     until the value is printed out */
    long startTime;
    String[] temp = null;

    public ReadMindWave() {
        System.out.println("ThinkGear DLL version: " + tg.TG_GetDriverVersion());

        connID = connect(COM_PORT);
        tg.TG_EnableBlinkDetection(connID, 1);  // switch on 
        // tg.TG_EnableLowPassFilter(connID, 512);

    }

    public void run() {


        startTime = System.currentTimeMillis();
        long reportTime = startTime;
        int totalPacks = 0;
        while (!stop) {
            totalPacks += readPackets(connID);
            // if ((System.currentTimeMillis() - reportTime) > REPORT_INTERVAL) {
            if (!hasRaw(connID)) {
            } else if (!hasSignal(connID)) {
                System.out.println("low signal");
            } else // printWaves(connID);
            {
                printAllWaves(totalPacks);
                addQuequeBean();
            }

            reportTime = System.currentTimeMillis();
        }


        //}

        tg.TG_FreeConnection(connID);
    }  // end of ReadMindWave()

    private int connect(int comPort) // connect to the MindWave, returning the connection ID
    {
        int connID = tg.TG_GetNewConnectionId();
        if (connID < 0) {
            System.out.println("Could not get a connection ID");
            System.exit(1);
        }

        int connResult = tg.TG_Connect(connID, "\\\\.\\COM" + comPort,
                ThinkGear.BAUD_115200, ThinkGear.STREAM_PACKETS);
        if (connResult < 0) {
            System.out.println("Could not connect to COM " + comPort);
            System.exit(1);
        }

        return connID;
    }  // end of connect()

    private void recPatterns() {

        try {
            String[] temp2 = getArray();
            if (!Arrays.equals(temp, temp2)) {
                csv.writeRecord(temp2);
            }
            temp = temp2;

        } catch (IOException ex) {

            Logger.getLogger(ReadMindWave.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private int readPackets(int connID) /* read as many packets are currently available. 
     TGCD will use them to update the currently values
     for the different Wave types and set their status to 'true'
     (i.e. updated). 
     */ {
        int numPackets = tg.TG_ReadPackets(connID, -1);    // read all available packets

        if (numPackets == -1) {
            System.out.println("Invalid connection ID");
            System.exit(1);
        } else if (numPackets == -2) {
            //  System.out.println("No packets available");
            return 0;
        } else if (numPackets == -3) {
            System.out.println("Serial stream read error");
            System.exit(1);
        }
        // else 
        //   System.out.println("No. packets: " + numPackets);

        if (isUpdated(connID, Wave.BlinkStrength)) {
            hasBlinked = true;
        }
        /* used to remember that there's been a blink strength update 
         until after the value has been printed */

        return numPackets;
    }  // end of readPackets()

    private void printWaves() // print information only about the attention, meditation 
    // and blink strength waves
    {
        System.out.println("---- Report ----");
        // System.out.println("  Signal: " + get(Wave.PoorSignal) );
        System.out.println("  Raw Signal: " + get(connID, Wave.Raw));
        System.out.println("  Attention: " + get(connID, Wave.Attention));
        System.out.println("  Meditation: " + get(connID, Wave.Meditation));
        if (hasBlinked) {
            System.out.println("  Blink Strength: " + get(connID, Wave.BlinkStrength));
            hasBlinked = false;    // reset flag after the value is printed
        }
    }  // end of printWaves()

    private void printAllWaves(int totalPacks) // print all the wave values and their update status
    {
        System.out.println("---- Report (" + totalPacks + ") ----");
        System.out.println(" " + "Figura N° " + imageFocus);
        for (Wave wave : Wave.values()) {
            System.out.println("  " + wave + ": " + get(connID, wave)
                    + " [" + isUpdated(connID, wave) + "]");
        }

    }  // end of printAllWaves()

    private String[] getArray() {
        String[] temp = new String[16];
        int i = 0;
        temp[15] = Double.toString(System.currentTimeMillis() - startTime);
        for (Wave wave : Wave.values()) {
            temp[i] = Double.toString(tg.TG_GetValue(connID, wave.getCode()));
            i++;
        }
        temp[14] = Figure.getName(imageFocus);
        temp[15] = Double.toString(System.currentTimeMillis() - startTime);
        return temp;

    }

    private void addQuequeBean() {
        if (isUpdated(connID, Wave.Raw)) {
            this.queque.add(new experienceBean(tg, connID, imageFocus, startTime));
        }
    }

    private String[] SetIntestation() {
        int i = 0;
        String[] temp = new String[16];
        for (Wave wave : Wave.values()) {
            temp[i] = wave + "";
            i++;
        }
        temp[14] = "Figura";
        temp[15] = "Tempo";
        return temp;
    }

    public List<experienceBean> freeQueuque() {
        List<experienceBean> temp = new ArrayList<experienceBean>();
        experienceBean tmp;
        while ((tmp = queque.poll()) != null) {
            temp.add(tmp);
        }
        return temp;
    }

    private double get(int connID, Wave wave) {
        return tg.TG_GetValue(connID, wave.getCode());
    }

    private boolean isUpdated(int connID, Wave wave) {
        return tg.TG_GetValueStatus(connID, wave.getCode()) != 0;
    }

    private boolean hasSignal(int connID) // is the signal strong enough? 
    {
        double signal = get(connID, Wave.PoorSignal);
        if (signal == 200) {   // special signal value
            System.out.println("ThinkGear contacts not touching the skin");
            return false;
        }
        return (signal < STRONG_SIGNAL);
    }  // end of hasSignal()

    private boolean hasRaw(int connID) /* if there's no raw data then there's a problem with the 
     the connection to the MindWave */ {
        return (get(connID, Wave.Raw) != 0);
    }

    public void stopWriting() {
        csv.close();
    }

    public int getImageFocus() {
        return imageFocus;
    }

    public void setImageFocus(int imageFocus) {
        this.imageFocus = imageFocus;
    }

    public boolean isStop() {
        return stop;
    }

    public void stop(boolean stop) {
        this.stop = stop;
    }
    // -----------------------------------------

    public static void main(String args[]) {
        new ReadMindWave();
    }
}  // end of ReadMindWave class

